import pandas as pd
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, confusion_matrix
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import precision_recall_curve, average_precision_score
from sklearn.metrics import roc_curve, auc
from sklearn.preprocessing import LabelEncoder
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties


# 读取Excel文件
def load_data(file_path):
    data = pd.read_excel(file_path, sheet_name='Sheet1', engine='openpyxl')
    data.columns = data.columns.astype(str)
    df = data.iloc[1:1801, 1:591]  # 根据实际情况调整
    return df

# 训练和评估SVM模型，并使用GridSearchCV进行参数调优
def train_evaluate_svm_with_gridsearch(df):
    X = df.drop('label', axis=1)  # 特征数据
    y = df['label']               # 标签数据

    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

    # 定义参数网格
    param_grid = {
        'C': [0.1, 1, 10, 100],
        'kernel': ['linear', 'rbf', 'poly'],
        'gamma': ['scale', 'auto'],
    }

    # 创建GridSearchCV实例
    grid_search = GridSearchCV(SVC(probability=True), param_grid, cv=5, verbose=1, pre_dispatch='2*n_jobs')

    # 将中文标签转换为数值型标签
    label_encoder = LabelEncoder()
    y_train_encoded = label_encoder.fit_transform(y_train)
    y_test_encoded = label_encoder.transform(y_test)

    # 训练模型，进行参数搜索
    grid_search.fit(X_train, y_train)


    # 打印最佳参数
    print(f"Best parameters: {grid_search.best_params_}")

    # 使用最佳估计器进行预测
    best_svm_classifier = grid_search.best_estimator_
    y_pred = best_svm_classifier.predict(X_test)
    y_pred_encoded = best_svm_classifier.predict(X_test)
    decision_values = best_svm_classifier.decision_function(X_test)

    # 计算准确率
    accuracy = accuracy_score(y_test, y_pred)
    print(f"Accuracy: {accuracy}")

    # 计算混淆矩阵
    confusion_mat = confusion_matrix(y_test, y_pred)

    # 假设你的类别标签如下，根据实际情况进行调整
    class_labels = ['地铁跑酷', '蛋仔派对', '金铲铲之战', '英雄联盟', '原神', '和平精英', '斗罗大陆', '第五人格', '王者荣耀']

    # 指定中文字体路径，这里使用的是常见的SimHei字体
    font_path = "SimHei.ttf"
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

    # 绘制带有类别标签的混淆矩阵
    plt.figure(figsize=(10, 8))
    ax = sns.heatmap(confusion_mat, annot=True, fmt='d', cmap='Blues', cbar=False,
                    xticklabels=class_labels, yticklabels=class_labels)

    # 设置图表标题和坐标轴标签
    ax.set_xlabel('预测标签', fontsize=14)
    ax.set_ylabel('真实标签', fontsize=14)
    ax.set_title('多类别分类的混淆矩阵', fontsize=16)

    # 显示图表
    plt.show()

    # 绘制PR曲线
    plt.figure(figsize=(10, 8))
    for class_index in range(len(class_labels)):
        # 计算当前类别的PR曲线
        precision, recall, _ = precision_recall_curve(y_test_encoded == class_index, decision_values[:, class_index])
        average_precision = average_precision_score(y_test_encoded == class_index, decision_values[:, class_index])
        
        # 绘制当前类别的PR曲线
        plt.plot(recall, precision, label=f'{class_labels[class_index]} (AP = {average_precision:0.2f})')

    plt.xlabel('Recall')
    plt.ylabel('Precision')
    plt.title('Precision-Recall Curve for Each Class')
    plt.legend(loc="lower right")
    plt.show()



    # 计算每个类别的预测概率
    y_score = best_svm_classifier.predict_proba(X_test)
    n_classes = y_score.shape[1]  # 获取类别的数量

    # 绘制ROC曲线
    plt.figure(figsize=(10, 8))
    for i in range(n_classes):
        # 为每个类别创建二进制的真实标签数组
        y_true_binary = (y_test_encoded == i).astype(int)
        
        # 确保至少有一个正样本，避免UndefinedMetricWarning
        if (y_true_binary == 1).any():
            # 计算ROC曲线和AUC
            fpr, tpr, _ = roc_curve(y_true_binary, y_score[:, i])
            roc_auc = auc(fpr, tpr)
            
            # 绘制ROC曲线
            plt.plot(fpr, tpr, label=f'Class {class_labels[i]} (AUC = {roc_auc:0.2f})')
        else:
            print(f'Warning: Class {class_labels[i]} has no positive samples in the test set.')
    plt.legend()
    plt.title('ROC Curves for Each Class')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.show()




    return best_svm_classifier, y_pred, accuracy, confusion_mat



def predict_and_append_labels(df, model):
    # 使用模型进行预测
    predictions = model.predict(df)
    
    # 将预测结果作为新列添加到数据框中
    df['predicted_label'] = predictions

    return df

# 主程序
if __name__ == "__main__":
    file_path = 'train_data.xlsx'  # 替换为你的Excel文件路径
    df = load_data(file_path)
    model, predictions, accuracy, confusion_matrix = train_evaluate_svm_with_gridsearch(df)

    # 使用训练的模型预测后续的数据
    #df2 = pd.read_excel('data.xlsx')
    #df_predictions = predict_and_append_labels(df2, model)  # 使用模型进行预测并添加标签

    # 如果需要，可以将更新后的DataFrame保存到新的Excel文件
    #df_predictions.to_excel('prediction_data.xlsx', index=True, engine='openpyxl')